/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */

#define PACKAGE "mcq"
#define VERSION "1.0.0"

/* Default path for a database, and its env home */
#define DBFILE "default.db"
#define DBHOME "/data1/memcachedb"

#define SELF_EID -3

#define DATA_BUFFER_SIZE 2048

#if defined(TCP_CORK) && !defined(TCP_NOPUSH)
#define TCP_NOPUSH TCP_CORK
#endif

struct stats {
    unsigned int curr_items;
    unsigned int total_items;
    unsigned long long curr_bytes;
    unsigned int curr_conns;
    unsigned int total_conns;
    unsigned int conn_structs;
    unsigned int get_cmds;
    unsigned int set_cmds;
    unsigned int get_hits;
    unsigned int get_misses;
    time_t started;        /* when the process was started */
    unsigned long long bytes_read;
    unsigned long long bytes_written;
};

struct settings {
    unsigned int maxbytes;
    int maxconns;
    int port;
    struct in_addr interface;
    int verbose;
    char *db_file;
    char *home;
};

struct db_settings {
    char *db_file;    /* db filename, where dbfile located. */
    char *env_home;    /* db env home dir path */
};

extern struct stats stats;
extern struct settings settings;

#define END_LEN 8001
#define END_LEN_STR "8000"

typedef struct _stritem {
    int nkey;
    int flag1;
    int flag2;
    int nbytes;            /* size of data */
    char end[END_LEN];
} item;

//#define ITEM_key(item) ((char*)&((item)->end[0]))
#define ITEM_key(item) ("k")

/* warning: don't use these macros with a function, as it evals its arg twice */
#define ITEM_data(item) ((char*) &(item)->end[0])
#define ITEM_ntotal(item) sizeof(struct _stritem)

enum conn_states {
    conn_listening,        /* the socket which listens for connections */
    conn_read,            /* reading in a command line */
    conn_write,            /* writing out a simple response */
    conn_nread,            /* reading in a fixed number of bytes */
    conn_swallow,        /* swallowing unnecessary bytes w/o storing */
    conn_closing,        /* closing this connection */
    conn_mwrite            /* writing out many items sequentially */
};

#define NREAD_ADD 1
#define NREAD_SET 2
#define NREAD_REPLACE 3

typedef struct {
    int sfd;
    int state;
    struct event event;
    short ev_flags;
    short which;        /* which events were just triggered */

    char *rbuf;
    int rsize;
    int rbytes;

    char *wbuf;
    char *wcurr;
    int wsize;
    int wbytes;
    int write_and_go;        /* which state to go into after finishing current write */
    void *write_and_free;    /* free this memory after finishing writing */
    char is_corked;        /* boolean, connection is corked */

    char *rcurr;
    int rlbytes;

    /* data for the nread state */

    /* 
     * item is used to hold an item structure created after reading the command
     * line of set/add/replace commands, but before we finished reading the actual 
     * data. The data is read into ITEM_data(item) to avoid extra copying.
     */

    item item;
    int item_comm;        /* which one is it: set/add/replace */

    /* data for the swallow state */
    int sbytes;            /* how many bytes to swallow */

    /* data for the mwrite state */
    int ipart;            /* 1 if we're writing a VALUE line, 2 if we're writing data */
    char ibuf[300];        /* for VALUE lines */
    char *iptr;
    int ibytes;

} conn;

/* listening socket */
extern int l_socket;

/* event handling, network IO */
void event_handler(int fd, short which, void *arg);
conn *conn_new(int sfd, int init_state, int event_flags);
void conn_close(conn * c);
void conn_init(void);
void drive_machine(conn * c);
int new_socket(void);
int server_socket(int port);
int update_event(conn * c, int new_flags);
int try_read_command(conn * c);
int try_read_network(conn * c);
void complete_nread(conn * c);
void process_command(conn * c, char *command);
/* stats */
void stats_reset(void);
void stats_init(void);

/* init */
void settings_init(void);

/* signal */
void sig_handler(int sig);
